/*
 * @file main.cpp
 * @author zhangpeihao
 */

#include <gtest/gtest.h>
#include <zsystem/core/common/types.h>
#include <zsystem/core/common/log.h>
#include <zsystem/service/sdo/metaobject.h>
#include "utcase.h"


#define TEST_all                0

#define TEST_objectFactory      1
#define TEST_tojson             1

#ifdef __WIN32__
const char* objectScheme = "..\\..\\unittest\\sdo\\objects.xml";
const char* propScheme = "..\\..\\unittest\\sdo\\props.xml";
#endif
#ifdef __LINUX__
const char* objectScheme = "./objects.xml";
const char* propScheme = "./props.xml";
#endif

#define TEST_OBJ_SYS  1
#define TEST_OBJ_ROOM 2
#define TEST_OBJ_USER 3

#define TEST_PROP_SYS_STATUS              1001
#define TEST_PROP_SYS_NAME                1002
#define TEST_PROP_ROOM_NAME               2001
#define TEST_PROP_ROOM_ID                 2002
#define TEST_PROP_ROOM_LOAD               2003
#define TEST_PROP_ROOM_USERS              2004
#define TEST_PROP_USER_ID                 3001
#define TEST_PROP_USER_NICKNAME           3002
#define TEST_PROP_USER_CONTRIBUTION       3003
#define TEST_PROP_USER_INCOME             3004
#define TEST_PROP_USER_CONTRIBUTIONLEVEL  3005
#define TEST_PROP_USER_INCOMELEVEL        3006

#define TEST_BUFFER_SIZE 65525

#if TEST_all || TEST_objectFactory
TEST_F(utcase, objectFactory)
{
  zsystem::metaObject* objSystem = zsystem::objectFactory::newObject(TEST_OBJ_SYS);
  ASSERT_TRUE(objSystem != Z_NULL);
  EXPECT_TRUE(objSystem->setStringProp(TEST_PROP_SYS_NAME, "Test"));
  EXPECT_TRUE(objSystem->setInt32Prop(TEST_PROP_SYS_STATUS, 1));

  Z_I32 ti32;
  Z_I64 ti64;
  char tstr[TEST_BUFFER_SIZE];
  Z_FLOAT tfloat;
  Z_I32 tlen(0);

  EXPECT_TRUE(objSystem->getStringProp(TEST_PROP_SYS_NAME, tstr, TEST_BUFFER_SIZE, tlen));
  EXPECT_STREQ("Test", tstr);

  EXPECT_TRUE(objSystem->getInt32(TEST_PROP_SYS_STATUS, ti32));
  EXPECT_EQ(1, ti32);

  zsystem::metaObject* objSystem2 = zsystem::objectFactory::newObject(TEST_OBJ_SYS);
  ASSERT_TRUE(objSystem2 != Z_NULL);
  EXPECT_TRUE(objSystem2->setStringProp(TEST_PROP_SYS_NAME, "Test2"));
  EXPECT_TRUE(objSystem2->setInt32Prop(TEST_PROP_SYS_STATUS, 2));
  EXPECT_TRUE((*objSystem) < (*objSystem2));

  EXPECT_TRUE(objSystem2->setInt32Prop(TEST_PROP_SYS_STATUS, 0));
  EXPECT_FALSE((*objSystem) < (*objSystem2));

  EXPECT_TRUE(objSystem2->setInt32Prop(TEST_PROP_SYS_STATUS, 1));
  EXPECT_FALSE((*objSystem) < (*objSystem2));
  EXPECT_FALSE((*objSystem2) < (*objSystem));

  zsystem::objectFactory::deleteObject(objSystem);
  zsystem::objectFactory::deleteObject(objSystem2);

  zsystem::metaObject* objRoom = zsystem::objectFactory::newObject(TEST_OBJ_ROOM);
  ASSERT_TRUE(objRoom != Z_NULL);
  EXPECT_TRUE(objRoom->setStringProp(TEST_PROP_ROOM_NAME, "Room 1"));
  EXPECT_TRUE(objRoom->setInt32Prop(TEST_PROP_ROOM_ID, 12345));
  EXPECT_TRUE(objRoom->setFloatProp(TEST_PROP_ROOM_LOAD, 0.23f));

  EXPECT_TRUE(objRoom->getStringProp(TEST_PROP_ROOM_NAME, tstr, TEST_BUFFER_SIZE, tlen));
  EXPECT_STREQ("Room 1", tstr);
  EXPECT_TRUE(objRoom->getInt32(TEST_PROP_ROOM_ID, ti32));
  EXPECT_EQ(12345, ti32);
  EXPECT_TRUE(objRoom->getFloat(TEST_PROP_ROOM_LOAD, tfloat));
  EXPECT_FLOAT_EQ(0.23f, tfloat);

  zsystem::objectFactory::deleteObject(objRoom);
}
#endif

#if TEST_all || TEST_tojson

#include <rapidjson/document.h>		// rapidjson's DOM-style API
#include <rapidjson/prettywriter.h>	// for stringify JSON
#include <rapidjson/filestream.h>	// wrapper of C stream for prettywriter as output
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>

struct userTestData{
  Z_I32 id;
  Z_I32 nickname_len;
  Z_I64 cont;
};

userTestData usersTestData[] = {
  {0, 0, 0},                                            // zero
  {0X80000000, 0, 0X8000000000000000LL},                // min
  {0X7FFFFFFF, 65400, 0X7FFFFFFFFFFFFFFFLL}             // max
};

struct floatData{
  Z_FLOAT f;
  Z_FLOAT check;
};

floatData floatTestData[] = {
  {0.0f, 0.0f},
  {0.1f, 0.1f},
  {-0.1f, -0.1f},
  {0.1234567f, 0.123457f},
  {1234567.0f, 1234570.0f}
};

TEST_F(utcase, tojson)
{
  zsystem::metaObject* user = zsystem::objectFactory::newObject(TEST_OBJ_USER);
  ASSERT_TRUE(user != Z_NULL);

  Z_I32 testNumber = sizeof(usersTestData) / sizeof(userTestData);
  for ( int i = 0; i < testNumber; i++)
  {
//    std::cout << "Test " << i << std::endl;
    EXPECT_TRUE(user->setInt32Prop(TEST_PROP_USER_ID, usersTestData[i].id));
    char tmpBuffer[TEST_BUFFER_SIZE];
    int j = 0;
    for ( ; j < usersTestData[i].nickname_len; j++)
    {
      tmpBuffer[j] = '0' + (j % 10);
    }
    tmpBuffer[j] = '\0';
    EXPECT_TRUE(user->setStringProp(TEST_PROP_USER_NICKNAME, tmpBuffer));
    EXPECT_TRUE(user->setInt64Prop(TEST_PROP_USER_CONTRIBUTION, usersTestData[i].cont));

    char buffer[TEST_BUFFER_SIZE] = {0};
    Z_I32 offset(0);
    EXPECT_TRUE(user->serializeToJson(buffer, TEST_BUFFER_SIZE, offset));
    buffer[offset] = '\0';

    // std::cout << "User : " << buffer << std::endl;

    rapidjson::Document document;
    ASSERT_FALSE(document.Parse<0>(buffer).HasParseError());

    EXPECT_TRUE(document.HasMember("3001"));
    EXPECT_TRUE(document["3001"].IsNumber());
    EXPECT_TRUE(document["3001"].IsInt());
    EXPECT_EQ(usersTestData[i].id, document["3001"].GetInt());

    EXPECT_TRUE(document.HasMember("3002"));
    EXPECT_TRUE(document["3002"].IsString());
    EXPECT_STREQ(tmpBuffer, document["3002"].GetString());

    EXPECT_TRUE(document.HasMember("3003"));
    EXPECT_TRUE(document["3003"].IsNumber());
    EXPECT_TRUE(document["3003"].IsInt64());
    EXPECT_EQ(usersTestData[i].cont, document["3003"].GetInt64());
  }

  zsystem::objectFactory::deleteObject(user);

  zsystem::metaObject* objRoom = zsystem::objectFactory::newObject(TEST_OBJ_ROOM);
  ASSERT_TRUE(objRoom != Z_NULL);
  testNumber = sizeof(floatTestData) / sizeof(floatData);
  for ( int i = 0; i < testNumber; i++)
  {
    EXPECT_TRUE(objRoom->setFloatProp(TEST_PROP_ROOM_LOAD, floatTestData[i].f));
    char buffer[TEST_BUFFER_SIZE] = {0};
    Z_I32 offset(0);
    EXPECT_TRUE(objRoom->serializeToJson(buffer, TEST_BUFFER_SIZE, offset));
    buffer[offset] = '\0';

//    std::cout << "Room : " << buffer << std::endl;

    rapidjson::Document document;
    ASSERT_FALSE(document.Parse<0>(buffer).HasParseError());

    EXPECT_TRUE(document.HasMember("2003"));
    EXPECT_TRUE(document["2003"].IsNumber());
    EXPECT_FLOAT_EQ(floatTestData[i].check, document["2003"].GetDouble());
  }
  zsystem::objectFactory::deleteObject(user);
}

#endif

int main(int argc, char **argv) 
{
  google::InitGoogleLogging(argv[0]);

#ifdef __WIN32__
    google::SetLogDestination(INFO, "..\\..\\..\\logs\\INFO_");
    google::SetLogDestination(WARNING, "..\\..\\..\\logs\\WARNING_");
    google::SetLogDestination(ERROR, "..\\..\\..\\logs\\ERROR_");
#endif
#ifdef __LINUX__
    google::SetLogDestination(INFO, "/tmp/INFO_");
    google::SetLogDestination(WARNING, "/tmp/WARNING_");
    google::SetLogDestination(ERROR, "/tmp/ERROR_");
#endif

  zsystem::objectFactory::init(objectScheme, propScheme);
  zsystem::objectFactory::isSchemeLoad();
  ::testing::InitGoogleTest(&argc, argv);
  int r = RUN_ALL_TESTS();
#ifdef __WIN32__
  getchar();
#endif
  google::ShutdownGoogleLogging();
  return r;
}
